package com.hsgene.logistics.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.hsgene.common.util.basic.JsonUtils;
import com.hsgene.common.util.basic.UUID32;
import com.hsgene.common.util.character.CharactersConstant;
import com.hsgene.common.util.date.SimpleDateUtils;
import com.hsgene.common.util.http.HttpUtil;
import com.hsgene.common.util.result.ApiResult;
import com.hsgene.common.util.security.MD5Utils;
import com.hsgene.generator.dto.GeneratorDto;
import com.hsgene.logistics.domain.LogisticsInfo;
import com.hsgene.logistics.dto.KuaiDi100DeatisInfo;
import com.hsgene.logistics.dto.KuaiDi100Info;
import com.hsgene.logistics.persistence.LogisticsPersistence;
import com.hsgene.logistics.redis.LogisticsRedisPersistence;
import com.hsgene.logistics.service.LogisticsService;
import com.hsgene.logistics.service.api.GeneratorService;
import org.apache.commons.lang.StringUtils;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

/**
 * 物流业务类
 *
 * @Author: wangxuefeng@hsgene.com
 * @Description:
 * @Date: Created in 9:43 2018/7/20
 * @Modified By:
 */
@Service
public class LogisticsServiceImpl implements LogisticsService {

    /**
     * LOG
     */
    private static Logger LOGGER = LogManager.getLogger(LogisticsServiceImpl.class);

    /**
     * 存redis物流单号
     */
    private static final String LOGISTICS_NUMBER = "logistics_number";

    /**
     * 授权公司编号
     */
    @Value("${kuaidi.num}")
    private String KUAIDI_NUM;

    /**
     * 授权key
     */
    @Value("${kuaidi.key}")
    private String KUAIDI_KEY;

    /**
     * 快递url
     */
    @Value("${kuaidi.url}")
    private String KUAIDI_URL;

    /**
     * redis list开始位置
     */
    private static int start = 0;

    /**
     * redis list结束位置
     */
    private static int end = 9;

    @Resource
    private LogisticsPersistence logisticsPersistence;

    @Resource
    private LogisticsRedisPersistence logisticsRedisPersistence;

    @Resource
    private GeneratorService generatorService;

    /**
     * 新增物流
     *
     * @param info 物流信息对象
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor =
            Exception.class)
    public int insert(LogisticsInfo info) {
        //获取生成id
        String id = getSeriaNumberId();
        info.setId(id);
        int result = logisticsPersistence.insert(info);
        //保存到redis,orderId为value
        logisticsRedisPersistence.saveLogisticsNumber(LOGISTICS_NUMBER, info.getOrderId());
        return result;
    }

    /**
     * 获取32位ID
     *
     * @return
     */
    private String getSeriaNumberId() {
        ApiResult<GeneratorDto> generatorResult = generatorService.getGenerator("default", null);
        if (!generatorResult.isSucc()) {
            return UUID32.randomUUIDString();
        }
        return generatorResult.getData().getSerialNumber();
    }

    /**
     * 查询物流
     *
     * @param orderId 订单号
     * @return
     */
    @Override
    public List<LogisticsInfo> queryLogisticsById(String orderId) {
        //1表示物流公司开始揽件,生成第一条物流信息
        LogisticsInfo info = logisticsPersistence.queryByLogisticsNumberAndStatus(orderId, 1);
        //表示数据还未录入,商家不知道快递单号+快递公司
        if (info == null) {
            return new ArrayList<>();
        }
        //获取快递单号
        String logisticsNumber = info.getExpressNumber();
        //生成时间+1个月比现在时间大,则时间间隔小于1个月,查询快递100,获取物流信息
        if (checkTime(info.getCreateDateTime())) {
            //查询快递100
            return getLogisticsInfosByKuaidi100(logisticsNumber, info.getExpressCompany(), orderId);
        }
        //查询数据库
        return logisticsPersistence.queryByLogisticsNumber(logisticsNumber);
    }

    /**
     * 花生仁查询物流信息
     *
     * @param provider  快递公司
     * @param expressNo 快递单号
     * @return
     */
    @Override
    public ApiResult<KuaiDi100Info> peanutLogisticsInfo(String provider, String expressNo) {
        KuaiDi100Info kuaiDi100Info = new KuaiDi100Info();
        try {
            //封装参数
            List<NameValuePair> listPair = getNameValuePairs(expressNo, provider);
            String result = HttpUtil.doPost(listPair, KUAIDI_URL, "UTF-8");
            if (StringUtils.isEmpty(result)) {
                kuaiDi100Info.setResult(false);
                kuaiDi100Info.setMessage("查询暂无结果");
            }
            //解析物流信息
            kuaiDi100Info = JsonUtils.jsonToObject(result, new TypeReference<KuaiDi100Info>() {
            });
            return new ApiResult(200, "success", "获取成功", kuaiDi100Info);
        } catch (Exception e) {
            e.printStackTrace();
            kuaiDi100Info.setResult(false);
            kuaiDi100Info.setReturnCode("500");
            kuaiDi100Info.setMessage("查询暂无结果");
            return new ApiResult(500, "failed", "获取信息失败", null);
        }
    }

    private List<LogisticsInfo> getLogisticsInfosByKuaidi100(String logisticsNumber, String expressCompany, String
            orderId) {
        //封装参数
        List<NameValuePair> listPair = getNameValuePairs(logisticsNumber, expressCompany);
        String result = HttpUtil.doPost(listPair, KUAIDI_URL, "UTF-8");
        if (StringUtils.isEmpty(result)) {
            return new ArrayList<>();
        }
        //解析物流信息
        KuaiDi100Info kuaiDi100Info = JsonUtils.jsonToObject(result, new TypeReference<KuaiDi100Info>() {
        });
        //将物流信息封装存入数据库
        List<KuaiDi100DeatisInfo> data = kuaiDi100Info.getData();
        if (data == null || data.isEmpty()) {
            return new ArrayList<>();
        }
        //解析快递100的结果信息
        return getLogisticsInfos(orderId, kuaiDi100Info, data);
    }

    private List<LogisticsInfo> getLogisticsInfos(String orderId, KuaiDi100Info kuaiDi100Info,
                                                  List<KuaiDi100DeatisInfo> data) {
        List<LogisticsInfo> logisticsInfos = analysisKuaiDi100Infos(orderId, kuaiDi100Info, data);
        //查看数据库是否已有完成的物流
        //3表明此次物流已结束,将所有信息插入数据库,否则不插入
        LogisticsInfo dataInfo = logisticsPersistence.queryByLogisticsNumberAndStatus(orderId, 3);
        if (dataInfo == null && "3".equals(kuaiDi100Info.getState())) {
            logisticsPersistence.insertLogisticsInfos(logisticsInfos);
        }
        return logisticsInfos;
    }

    private List<LogisticsInfo> analysisKuaiDi100Infos(String orderId, KuaiDi100Info kuaiDi100Info,
                                                       List<KuaiDi100DeatisInfo> data) {
        List<LogisticsInfo> logisticsInfos = new LinkedList<>();
        for (int i = 0; i < data.size(); i++) {
            LogisticsInfo logisticsInfo = new LogisticsInfo();
            logisticsInfo.setId(getSeriaNumberId());
            //0在途、1揽件、2疑难、3签收、4退签、5派件、6退回
            //最后一条物流信息未结束物流信息
            if (i == 0 && kuaiDi100Info.getState().equals("3")) {
                logisticsInfo.setExpressCurrentStatus(3);
                logisticsInfo.setLogisticsIsEnd(0);
            } else {
                logisticsInfo.setExpressCurrentStatus(0);
                logisticsInfo.setLogisticsIsEnd(1);
            }
            //0物流单暂无结果、1查询成功、2接口出现异常
            logisticsInfo.setExpressResultStatus(1);
            logisticsInfo.setExpressCompany(kuaiDi100Info.getCom());
            logisticsInfo.setExpressNumber(kuaiDi100Info.getNu());
            Date date = SimpleDateUtils.parseDate(data.get(i).getTime(), CharactersConstant.YYYY_MM_DD_HH_MM_SS, null);
            logisticsInfo.setLogisticsTrackingTime(date);
            logisticsInfo.setCreateDateTime(date);
            logisticsInfo.setUpdateDateTime(date);
            logisticsInfo.setLogisticsTrackingDescription(data.get(i).getContext());
            if (StringUtils.isNotEmpty(orderId)) {
                logisticsInfo.setOrderId(orderId);
            }
            logisticsInfos.add(logisticsInfo);
        }
        return logisticsInfos;
    }

    private List<NameValuePair> getNameValuePairs(String logisticsNumber, String company) {
        //查询快递100
        Map<String, String> map = new HashMap<>();
        //快递公司
        map.put("com", company);
        //快递单号
        map.put("num", logisticsNumber);
        String param = JsonUtils.objectToJson(map);
        String sign = MD5Utils.getMD5Code(param + KUAIDI_KEY + KUAIDI_NUM).toUpperCase();
        List<NameValuePair> listPair = new ArrayList<>();
        NameValuePair paramValue = new BasicNameValuePair("param", param);
        NameValuePair customerValue = new BasicNameValuePair("customer", KUAIDI_NUM);
        NameValuePair signValue = new BasicNameValuePair("sign", sign);
        listPair.add(paramValue);
        listPair.add(customerValue);
        listPair.add(signValue);
        return listPair;
    }

    /**
     * 每天凌晨2点定时更新更新
     */
    @Scheduled(cron = "0 0 2 * * ?")
    //@Scheduled(fixedDelay = 5000)
    protected void updateLogistics() {
        List<String> result = logisticsRedisPersistence.getLogisticsFromRedis(LOGISTICS_NUMBER, start, end);
        if (result == null || result.isEmpty()) {
            start = 0;
            end = 0;
            return;
        }
        start += 10;
        end += 10;
        //遍历查看redis中存在的物流数据库是否有已经完成的物流
        result.stream().forEach(orderId -> {
            //3表示物流以完成
            LogisticsInfo info = logisticsPersistence.queryByLogisticsNumberAndStatus(orderId, 3);
            //为null,说明数据库无此订单的完成信息,查询快递100
            if (info == null) {
                //获取物流单号
                LogisticsInfo dataInfo = logisticsPersistence.queryByLogisticsNumberAndStatus(orderId, 1);
                if (dataInfo != null) {
                    getLogisticsInfosByKuaidi100(dataInfo.getExpressNumber(), dataInfo.getExpressNumber(), orderId);
                }
            } else {
                //redis移除此条物流编号
                LOGGER.info("remove redis order id is :" + orderId);
                logisticsRedisPersistence.remove(LOGISTICS_NUMBER, orderId);
                start--;
                end--;
            }
        });
    }

    private boolean checkTime(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MONTH, 1);
        return calendar.getTime().after(new Date());
    }

}
